package ast

import (
	"fmt"
	"strings"

	"gitee.com/u-language/u-language/ucom/enum"
	"gitee.com/u-language/u-language/ucom/errcode"
	"gitee.com/u-language/u-language/ucom/internal/errutil"
	"gitee.com/u-language/u-language/ucom/internal/utils"
	"gitee.com/u-language/u-language/ucom/lex"
)

// try_AutoFreeIn 尝试处理自动释放块内的函数调用
func try_AutoFreeIn(old, i int, ret []Node, mempool Expr) {
	for index := old + 1; index < i; index++ {
		if utils.IsNil(ret[index]) {
			continue
		}
		switch n := ret[index].(type) {
		case *DefaultNode, RbraceNode, *Object, *Objects, *GotoStmt, *LabelNode, BreakStmt, ContinueStmt, *StructDecl, *EnumDecl:
		case *CallNode:
			try_AutoFreeCallFunc(n, mempool)
		case *VarNode:
			try_AutoFreeCallFunc(n.Value, mempool)
		case *ASSIGNNode:
			try_AutoFreeCallFunc(n.Src, mempool)
			try_AutoFreeCallFunc(n.Dest, mempool)
		case *ForNode:
			try_AutoFreeCallFunc(n.InitStmt.(*VarNode).Value, mempool)
			try_AutoFreeCallFunc(n.BoolExpr, mempool)
			if assign, ok := n.EndStmt.(*ASSIGNNode); ok {
				try_AutoFreeCallFunc(assign.Dest, mempool)
				try_AutoFreeCallFunc(assign.Src, mempool)
			}
		case *OpExpr:
			try_AutoFreeCallFunc(n.Src1, mempool)
			try_AutoFreeCallFunc(n.Src2, mempool)
		case *IfNode:
			try_AutoFreeCallFunc(n.BoolExpr, mempool)
		case *ElseNode:
			try_AutoFreeCallFunc(n.BoolExpr, mempool)
		case *ReturnNode:
			try_AutoFreeCallFunc(n.RetValue, mempool)
		case *ConstNode:
			try_AutoFreeCallFunc(n.Value, mempool)
		case *SelfOpStmt:
			try_AutoFreeCallFunc(n.Dest, mempool)
		case *SwitchNode:
			try_AutoFreeCallFunc(n.Expr, mempool)
		case *CaseNode:
			try_AutoFreeCallFunc(n.Expr, mempool)
		case *AutoFreeNode:
			try_AutoFreeCallFunc(n.Expr, mempool)
		case *IndexExpr:
			try_AutoFreeCallFunc(n.Index, mempool)
			try_AutoFreeCallFunc(n.X, mempool)
		case *Dereference:
			try_AutoFreeCallFunc(n.Value, mempool)
		default:
			panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的节点：%+v", ret[index])))
		}
	}
}

// try_AutoFreeCallFunc 尝试为自动释放块处理需要处理的函数调用
func try_AutoFreeCallFunc(n Expr, mempool Expr) {
	if utils.IsNil(n) {
		return
	}
	switch n := n.(type) {
	case *CallNode:
		for i := 0; i < len(n.Parame); i++ {
			try_AutoFreeCallFunc(n.Parame[i], mempool)
		}
		if utils.Is_In_AutoFree(n.FuncName.FuncName()) { //如果是在自动释放块内被调用的自定义函数
			rewriteAutoFreeCallParameList(&n.Parame, mempool)
			return
		}
		if o, ok := n.FuncName.(*Object); ok { //如果是malloc或mallocSize，改为调用mempool__Mempool_New
			switch o.Name {
			case enum.Malloc:
				o.Name = enum.MemPoolNew
				if len(n.Parame) != 0 {
					if o, ok := n.Parame[0].(*Object); ok {
						o.Kind = TypeObj
					}
				}
				rewriteAutoFreeCallParameList(&n.Parame, mempool)
			case enum.MallocSize:
				o.Name = enum.MemPoolNew
				rewriteAutoFreeCallParameList(&n.Parame, mempool)
			}
		}
	case *OpExpr:
		try_AutoFreeCallFunc(n.Src1, mempool)
		try_AutoFreeCallFunc(n.Src2, mempool)
	case *Dereference:
		try_AutoFreeCallFunc(n.Value, mempool)
	case *IndexExpr:
		try_AutoFreeCallFunc(n.Index, mempool)
		try_AutoFreeCallFunc(n.X, mempool)
	case *Object, *Objects:
	default:
		panic(errutil.NewErr2(errutil.CompileErr, fmt.Sprintf("未知的节点：%+v", n)))
	}
}

// rewriteAutoFreeCallParameList 重写参数列表
// 如果开头不是  generate开头的变量，添加 mempool参数到开头
func rewriteAutoFreeCallParameList(parame *[]Expr, mempool Expr) {
	if len(*parame) > 0 {
		if o, ok := (*parame)[0].(*Object); ok {
			if strings.HasSuffix(o.Name, enum.Generate) { //如果已经有了mempool
				return
			}
		}
	}
	(*parame) = append([]Expr{mempool}, (*parame)...)
}

// 解析自动释放块
//
// 实现假定 line[0].Value[0].TYPE==AutoFree
func parserAutoFree(t *Tree, line *lex.Line, sbt *Sbt, InAutoFree bool, FuncInfo *FuncInfo) *AutoFreeNode {
	if !check_lex_LBRACE(t, line, errcode.AutoFreeErr) { //语法规定位置没有左大括号
		return nil
	}
	llen := len(line.Value)
	if llen < 3 { //正确的自动释放块 autofree size {  至少三个token
		if llen == 2 { //既有autofree也有{，就是没有size
			t.Panic(line, nil, errcode.AutoFreeErr, errcode.NoExpr)
		} else {
			t.Panic(line, nil, errcode.AutoFreeErr, errcode.OPbug)
		}
		return nil
	}
	t.IsHaveAutoFree.Store(true)
	ret := NewAutoFreeNode(t.Filename)
	ret.Expr = parserExprNode(t, lex.NewLine(line.Linenum, line.Value[1:len(line.Value)-1]), errcode.AutoFreeErr, InAutoFree, FuncInfo)
	sbt.AddVar(utils.Generate_mempool_Name(line.Linenum, t.Filename), mempool, true, line.Linenum, t.Filename)
	ret.Sbt.seniorSbt(sbt)
	return ret
}

var mempool = NewObject(SymbolObj, enum.MemPool)
